#include <windows.h>
#include <stdio.h>
#include "exp_api.h"
#include "expng.h"

#define PNG_TEXT_SUPPORTED
#define PNG_iTXt_SUPPORTED
#define PNG_WRITE_iTXt_SUPPORTED
#include "lib/png.h"
#pragma comment(lib, "lib/libpng.lib")
#pragma comment(lib, "lib/zlib.lib")

//---------------------------------------------------------------------------
int __stdcall GetPluginInfo(int infono, char *buf, int buflen)
{
    char *infomation[] = {
      /* 0 */  "T0XN",
      /* 1 */  "PNG export library v0.0.3.8",
      /* 2 */  "PNG",
      /* 3 */  ".png",
    };
    if (infono < 0 || infono >= (sizeof(infomation) / sizeof(char *))) 
        return 0;

    lstrcpyn(buf, infomation[infono], buflen);

    return lstrlen(buf);
}
//---------------------------------------------------------------------------
int __stdcall IsSupported(int colorDepth)
{
	extern expng_opt opt;
    /* 色深度を確認 */
    if (   colorDepth == 1
		|| colorDepth == 4
		|| colorDepth == 8
		|| colorDepth == 24
		) 
    {
		return 1;
	}
    if (   colorDepth == 32
		&& opt.alpha == TRUE
		) 
    {
		return 1;
	}

    return 0;
}
//---------------------------------------------------------------------------
#if 0
void latincpy(char *dest, char *src)
{
    unsigned char *ps = (unsigned char *)src;
    unsigned char *pd = (unsigned char *)dest;
    for (;;) {
		if (*ps == '\0') break;
		if (*ps == 0x0d) {
            ps++;
    		if (*ps == '\0') break;
			if (*ps == 0x0a) {
                *(pd++) = *(ps++);
				continue;
			}
            *(pd++) = 0x0a;
			continue;
		}	
        *(pd++) = *(ps++);
    }
}
#endif
//---------------------------------------------------------------------------
int write_png(char *filepath, BITMAPINFOHEADER *bmih, char *pBm, 
			  PictureInfo *lpInfo, expng_opt *opt,
    int (CALLBACK *lpPrgressCallback)(int,int,long), long lData)
{
	int ret = XPI_ALL_RIGHT;

    FILE *fp;
    png_structp png_ptr;
    png_infop info_ptr;
    int width, height,dep;
    int type,i_val;
    int DIBmx;
    int bit_depth;
	int ccount;
   	png_color *palette = NULL;
	int number_passes=0,/*interlacing=FALSE,*/pass;
	int pg;//,maxpg;
	int y;
	//png_text text_ptr[1];
	//wchar_t *pwc = NULL;
	//char *pc = NULL;//"testコメントテスト";
	//char *pcom = NULL;
	//unsigned int comsize;
	//DWORD err=0;
	//char *nulstr = "";

    width = bmih->biWidth;
    height = bmih->biHeight;
	if (height < 0) height = -height;
    dep = bmih->biBitCount;
	ccount = bmih->biClrUsed;

	if (    IsSupported(dep) == 0
	     || bmih->biCompression != BI_RGB
	) {
		return XPI_NOT_SUPPROT;
	}

    /* open the file */
	fp = fopen(filepath, "wb");
    if (fp == NULL) return XPI_FILE_WRITE_ERROR;

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
				      (void *)NULL,/*user_error_ptr*/ NULL,NULL);

    if (png_ptr == NULL) {
        fclose(fp);
        return XPI_OTHER_ERROR;
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL) {
        fclose(fp);
        png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
        return XPI_OTHER_ERROR;
    }

    if (setjmp(png_ptr->jmpbuf)) {
        fclose(fp);
        png_destroy_write_struct(&png_ptr, &info_ptr);
        return XPI_OTHER_ERROR;
    }

    png_init_io(png_ptr, fp);

	png_set_compression_level(png_ptr, opt->complevel); 

    png_set_filter(png_ptr, 0, opt->filter);

    bit_depth = (dep>8)? 8: dep;
    if (dep <= 8) {
		type = PNG_COLOR_TYPE_PALETTE;
    } else if (dep == 32) {
    	type = PNG_COLOR_TYPE_RGB_ALPHA;
    } else {
    	type = PNG_COLOR_TYPE_RGB;
    }
    if (opt->inter) {
		i_val = PNG_INTERLACE_ADAM7;
    } else {
		i_val = PNG_INTERLACE_NONE;
    }
	png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, type,
      i_val, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
#if 0 //コメント埋め込み
if (lpInfo->hInfo != NULL) {
    /* Optionally write comments into the image */
	//まずUnicodeに変換
#if 0
    comsize = LocalSize(lpInfo->hInfo);
    pc = (char *)calloc(1, comsize);
	pcom = LocalLock(lpInfo->hInfo);
	latincpy(pc, pcom);
	LocalUnlock(lpInfo->hInfo);
	comsize = strlen(pc);
    pwc = (WCHAR *)calloc(2, comsize);
	comsize *= 2;
    err = MultiByteToWideChar(CP_ACP, 0, pc, -1, pwc, comsize);
    comsize = WideCharToMultiByte(CP_UTF8, 0, pwc, -1, pcom, 0, NULL, NULL);
    pcom = calloc(1, comsize);
    err = WideCharToMultiByte(CP_UTF8, 0, pwc, -1, pcom, comsize, NULL, NULL);
    text_ptr[0].compression = PNG_ITXT_COMPRESSION_NONE;
    text_ptr[0].itxt_length = strlen(pcom);
    text_ptr[0].text_length = 0;//comsize;//
#else
	pc = LocalLock(lpInfo->hInfo);
	comsize = LocalSize(lpInfo->hInfo);
    pcom = calloc(1, comsize);
	strcpy(pcom, pc);
	LocalUnlock(lpInfo->hInfo);
    text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
    text_ptr[0].text_length = strlen(pcom);//
#endif
	text_ptr[0].key = "Comment";
    text_ptr[0].text = pcom;
	//text_ptr[0].text_length = comsize;
    //text_ptr[0].itxt_length = comsize;
    text_ptr[0].lang = opt->lang;
    text_ptr[0].lang_key = "Comment";
    png_set_text(png_ptr, info_ptr, text_ptr, 1);
	if (pc != NULL)  free(pc);
	if (pwc != NULL)  free(pwc);
	if (pcom != NULL) free(pcom);
}
#endif

	switch (dep) {
	    case 32:
		    DIBmx = width*4;
		    break;
	    case 24:
		    DIBmx = ((width*3)+3)/4*4;
		    break;
	    //case 16:
		    //DIBmx = (width+1)/2*4;
		    //break;
	    case 8:
		    DIBmx = (width+3)/4*4;
		    break;
		case 4:
			DIBmx = (width+7)/8*4;
			break;
		case 1:
			DIBmx = (width+31)/32*4;
			break;
	}
    if (dep <= 8) {
   		RGBQUAD *colmap;
   		int i, npal;

		npal = (1<<dep);
		if (!ccount) ccount = npal;

   		colmap = ((BITMAPINFO *)bmih)->bmiColors;
		palette = png_malloc(png_ptr, npal * sizeof (png_color));
   		for (i=0; i < ccount; i++) {
   			palette[i].red   = colmap[i].rgbRed;
   			palette[i].blue  = colmap[i].rgbBlue;
   			palette[i].green = colmap[i].rgbGreen;
   		}
   		for (; i < npal; i++) {
   			palette[i].red   = 0;
   			palette[i].blue  = 0;
   			palette[i].green = 0;
   		}
	    png_set_PLTE(png_ptr, info_ptr, palette, npal);
    }

    png_write_info(png_ptr, info_ptr);

    if (   type == PNG_COLOR_TYPE_RGB 
		|| type == PNG_COLOR_TYPE_RGB_ALPHA
	) {
        png_set_bgr(png_ptr);
	}


    if (opt->inter) {
        number_passes = png_set_interlace_handling(png_ptr);
    } else {
        number_passes = 1;
    }

    //if (lpPrgressCallback != NULL) lpPrgressCallback(0,0,lData);
    //maxpg=height*number_passes;
    pg=0;

    for (pass = 0; pass < number_passes; pass++) {
        /* Write a few rows at a time. */
        //png_write_rows(png_ptr, row_pointers, number_of_rows);

        /* If you are only writing one row at a time, this works */
        for (y = 0; y < height; y++) {
            png_bytep row_pointers = &(((png_bytep)pBm)[(height-1-y)*DIBmx]);

      	    //if (lpPrgressCallback != NULL) lpPrgressCallback(pg+1,maxpg,lData);
            png_write_rows(png_ptr, &row_pointers, 1);
            pg++;
		}
    }
    png_write_end(png_ptr, info_ptr);

    png_free(png_ptr, palette);
    png_destroy_write_struct(&png_ptr, &info_ptr);

	fclose(fp);

	//if (lpPrgressCallback != NULL) lpPrgressCallback(maxpg,maxpg,lData);

    return ret;
}
//---------------------------------------------------------------------------
int __stdcall CreatePicture (
                char *filepath, unsigned int flag,
                HANDLE *pHBInfo, HANDLE *pHBm, PictureInfo *lpInfo,
    int (CALLBACK *lpPrgressCallback)(int,int,long),
                long lData)
{
	extern expng_opt opt;
	int ret;
	BITMAPINFOHEADER *bmih;
	char *pBm;

    /* flag check */
	switch (flag) {
		case 0: //メモリハンドル
		    bmih = (BITMAPINFOHEADER*)LocalLock(*pHBInfo);
			pBm = (char *)LocalLock(*pHBm);
			break;
		case 1: //メモリのアドレス
		    bmih = (BITMAPINFOHEADER*)pHBInfo;
			pBm = (char *)pHBm;
			break;
		default:
    		return XPI_NO_FUNCTION;
	}

	ret = write_png(filepath, bmih, pBm, lpInfo, &opt, lpPrgressCallback, lData);

	if (flag == 0) {
		//メモリハンドル
		LocalUnlock(*pHBInfo);
		LocalUnlock(*pHBm);
	}

	return ret;
}
//---------------------------------------------------------------------------
